require 'pp'
require 'csv'

class TermsController < ApplicationController

  def update_index
    start_time = Time.now
    Term.delete_all
    
    path_to_terms_index = "#{File.dirname(File.expand_path($0))}/terms_index.csv"
    file = File.new(path_to_terms_index, "w")
    
    num_words = 0
    Link.find(:all).each do |link|
      words = parse_words(link.e_to) << parse_words(link.e_from) << parse_words(link.body)
      words.flatten!
      term_paramsets = []
      words.each_index do |pos|
        word = '"' + words[pos] + '"'
        file << "#{word}, #{link.id}, #{pos}\n"
      end
      num_words = num_words + words.size
    end
    file.close   # write to disk
      
    query = "COPY terms (term, email_id, pos) FROM '#{path_to_terms_index}' CSV"
    sql = ActiveRecord::Base.connection()
  	sql.begin_db_transaction
  	sql.execute(query)
  	sql.commit_db_transaction
    
    elapsed_time = Time.now - start_time
    @indexing_results = "Created #{num_words} terms from #{Link.count} emails in #{elapsed_time} seconds"
  end

  #
  # TA note:
  # this method is not used, but rather here for your edification. 
  # it will create the inverted index, but is SUPER slow. 
  #
  def update_index_naive
    Term.delete_all
    count = 0
    Term.transaction do
      Link.find(:all).each do |link|
        words = []
        words = words << parse_words(link.e_to)
        words = words << parse_words(link.e_from)
        words = words << parse_words(link.body)
        words.flatten!
        term_paramsets = []
        words.each_index do |pos|
          word = words[pos]
          term_paramsets << { :term => word, :email_id => link.id, :pos => pos}
        end
        terms = Term.create(term_paramsets)
        count += terms.size
      end
    end
    @indexing_results = "Created #{count} terms from #{Link.count} emails"
  end

  def search
    @query = params[:query]

    if !@query.to_s.strip.empty?      
      tokens =  parse_words(@query)
      @all_results = Link.find_by_sql(build_sql_query_joins(tokens))
      build_graph(@all_results)
    end
  end

  def build_sql_query_joins(tokens)
    vars = (1..tokens.size).to_a.map{|n| "t#{n}"}
    from = "links l, " + vars.map{|var| "terms #{var}"}.join(", ")
    where = if tokens.size == 1
      "t1.term = '#{tokens.first}'"
    else
      ((1...tokens.size).map{|n| "t#{n}.email_id = t#{n+1}.email_id"} +
        (1..tokens.size).map{|n| "t#{n}.term = '#{tokens[n-1]}'"}).join(" AND ")
    end
    "SELECT DISTINCT(l.id), l.e_to, l.e_from, l.body FROM #{from} WHERE l.id = t1.email_id AND #{where}"
  end
  
  def build_sql_query_intersects(tokens)
    email_ids_table = tokens.map{|token| "SELECT email_id FROM terms t WHERE t.term = '#{token}'"}.join(" INTERSECT ")
    "SELECT l.* FROM links l, (#{email_ids_table}) e WHERE l.id = e.email_id"
  end
  
  #
  # TA note:
  # below is mostly boiler-plate RoR code
  # we did add in pagination of results
  #
  
  # GET /terms
  # GET /terms.xml
  def index
    @terms = Term.paginate :page => params[:page]    
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @terms }
    end
  end

  # GET /terms/1
  # GET /terms/1.xml
  def show
    @term = Term.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @term }
    end
  end

  # GET /terms/new
  # GET /terms/new.xml
  def new
    @term = Term.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @term }
    end
  end

  # GET /terms/1/edit
  def edit
    @term = Term.find(params[:id])
  end

  # POST /terms
  # POST /terms.xml
  def create
    @term = Term.new(params[:term])

    respond_to do |format|
      if @term.save
        flash[:notice] = 'Term was successfully created.'
        format.html { redirect_to(@term) }
        format.xml  { render :xml => @term, :status => :created, :location => @term }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @term.errors, :status => :unprocessable_entity }
      end
    end
  end

  # PUT /terms/1
  # PUT /terms/1.xml
  def update
    @term = Term.find(params[:id])

    respond_to do |format|
      if @term.update_attributes(params[:term])
        flash[:notice] = 'Term was successfully updated.'
        format.html { redirect_to(@term) }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @term.errors, :status => :unprocessable_entity }
      end
    end
  end

  # DELETE /terms/1
  # DELETE /terms/1.xml
  def destroy
    @term = Term.find(params[:id])
    @term.destroy

    respond_to do |format|
      format.html { redirect_to(terms_url) }
      format.xml  { head :ok }
    end
  end
end
